From 094c537001c4618aad71e890af937013d06a579a Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 21 Nov 2019 21:20:16 +0100 Subject: [PATCH] builder: Add gtk_builder_create_closure() This will be the future way to connect signals automatically (and be used for other things, too). For now, gtk_builder_connect_signals_default() is ported to use it. --- docs/reference/gtk/gtk4-sections.txt | 2 + gtk/gtkbuilder.c | 150 +++++++++++++++++++++++---- gtk/gtkbuilder.h | 21 +++- 3 files changed, 150 insertions(+), 23 deletions(-) diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 04bf68f83b..c9fefcebf1 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -529,6 +529,8 @@ gtk_builder_new_from_string gtk_builder_add_callback_symbol gtk_builder_add_callback_symbols gtk_builder_lookup_callback_symbol +gtk_builder_create_closure +gtk_builder_create_cclosure gtk_builder_add_from_file gtk_builder_add_from_resource gtk_builder_add_from_string diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c index 5538ecfe8c..5c00a50570 100644 --- a/gtk/gtkbuilder.c +++ b/gtk/gtkbuilder.c @@ -1658,31 +1658,23 @@ gtk_builder_connect_signals_default (GtkBuilder *builder, GConnectFlags flags, gpointer user_data) { - GCallback func; + GClosure *closure; + GError *error = NULL; - func = gtk_builder_lookup_callback_symbol (builder, handler_name); + closure = gtk_builder_create_closure (builder, + handler_name, + flags & G_CONNECT_SWAPPED ? TRUE : FALSE, + connect_object, + &error); - if (!func) + if (closure == NULL) { - GModule *module = gtk_builder_get_module (builder); - - /* Only error out for missing GModule support if we've not - * found the symbols explicitly added with gtk_builder_add_callback_symbol() - */ - if (module == NULL) - g_error ("gtk_builder_connect_signals() requires working GModule"); - - if (!g_module_symbol (module, handler_name, (gpointer)&func)) - { - g_warning ("Could not find signal handler '%s'. Did you compile with -rdynamic?", handler_name); - return; - } + g_warning ("%s", error->message); + g_error_free (error); + return; } - if (connect_object) - g_signal_connect_object (object, signal_name, func, connect_object, flags); - else - g_signal_connect_data (object, signal_name, func, user_data, NULL, flags); + g_signal_connect_closure (object, signal_name, closure, flags & G_CONNECT_AFTER ? TRUE : FALSE); } @@ -2571,7 +2563,7 @@ _gtk_builder_get_template_type (GtkBuilder *builder) * * Adds the @callback_symbol to the scope of @builder under the given @callback_name. * - * Using this function overrides the behavior of gtk_builder_connect_signals() + * Using this function overrides the behavior of gtk_builder_create_closure() * for any callback symbols that are added. Using this method allows for better * encapsulation as it does not require that callback symbols be declared in * the global namespace. @@ -2666,6 +2658,122 @@ gtk_builder_lookup_callback_symbol (GtkBuilder *builder, return g_hash_table_lookup (priv->callbacks, callback_name); } +static GClosure * +gtk_builder_create_closure_for_funcptr (GtkBuilder *builder, + GCallback callback, + gboolean swapped, + GObject *object) +{ + GClosure *closure; + + if (object) + { + if (swapped) + closure = g_cclosure_new_object_swap (callback, object); + else + closure = g_cclosure_new_object (callback, object); + } + else + { + if (swapped) + closure = g_cclosure_new_swap (callback, NULL, NULL); + else + closure = g_cclosure_new (callback, NULL, NULL); + } + + return closure; +} + +/** + * gtk_builder_create_closure: + * @builder: a #GtkBuilder + * @function_name: name of the function to look up + * @swapped: %TRUE to create a swapped closure + * @object: (nullable): Object to create the closure with + * @error: (allow-none): return location for an error, or %NULL + * + * Creates a closure to invoke the function called @function_name. + * + * If a closure function was set via gtk_builder_set_closure_func(), + * will be invoked. + * Otherwise, gtk_builder_create_cclosure() will be called. + * + * If no closure could be created, %NULL will be returned and @error will + * be set. + * + * Returns: (nullable): A new closure for invoking @function_name + **/ +GClosure * +gtk_builder_create_closure (GtkBuilder *builder, + const char *function_name, + gboolean swapped, + GObject *object, + GError **error) +{ + g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL); + g_return_val_if_fail (function_name, NULL); + g_return_val_if_fail (object == NULL || G_IS_OBJECT (object), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + return gtk_builder_create_cclosure (builder, function_name, swapped, object, error); +} + +/** + * gtk_builder_create_cclosure: (skip) + * @builder: a #GtkBuilder + * @function_name: name of the function to look up + * @swapped: %TRUE to create a swapped closure + * @object: (nullable): Object to create the closure with + * @error: (allow-none): return location for an error, or %NULL + * + * This is the default function used by gtk_builder_set_closure_func(). Some bindings + * with C support may want to call this function as a fallback from their closure + * function. + * + * This function has no purpose otherwise. + * + * This function will prefer callbacks added via gtk_builder_add_callback_symbol() + * to looking up public symbols. + * + * Returns: (nullable): A new closure for invoking @function_name + **/ +GClosure * +gtk_builder_create_cclosure (GtkBuilder *builder, + const char *function_name, + gboolean swapped, + GObject *object, + GError **error) +{ + GModule *module = gtk_builder_get_module (builder); + GCallback func; + + func = gtk_builder_lookup_callback_symbol (builder, function_name); + if (func) + return gtk_builder_create_closure_for_funcptr (builder, func, swapped, object); + + if (module == NULL) + { + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_FUNCTION, + "Could not look up function `%s`: GModule is not supported.", + function_name); + return NULL; + } + + if (!g_module_symbol (module, function_name, (gpointer)&func)) + { + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_FUNCTION, + "No function named `%s`.", + function_name); + return NULL; + } + + return gtk_builder_create_closure_for_funcptr (builder, func, swapped, object); +} + /** * gtk_builder_new_from_file: * @filename: filename of user interface description file diff --git a/gtk/gtkbuilder.h b/gtk/gtkbuilder.h index f3353cedcf..fb192dc4a9 100644 --- a/gtk/gtkbuilder.h +++ b/gtk/gtkbuilder.h @@ -63,7 +63,10 @@ typedef struct _GtkBuilderClass GtkBuilderClass; * @GTK_BUILDER_ERROR_TEMPLATE_MISMATCH: The wrong type was specified in a composite class’s template XML * @GTK_BUILDER_ERROR_INVALID_PROPERTY: The specified property is unknown for the object class. * @GTK_BUILDER_ERROR_INVALID_SIGNAL: The specified signal is unknown for the object class. - * @GTK_BUILDER_ERROR_INVALID_ID: An object id is unknown + * @GTK_BUILDER_ERROR_INVALID_ID: An object id is unknown. + * @GTK_BUILDER_ERROR_INVALID_FUNCTION: A function could not be found. This often happens + * when symbols are set to be kept private. Compiling code with -rdynamic or using the + * `gmodule-export-2.0` pkgconfig module can fix this problem. * * Error codes that identify various errors that can occur while using * #GtkBuilder. @@ -83,7 +86,8 @@ typedef enum GTK_BUILDER_ERROR_TEMPLATE_MISMATCH, GTK_BUILDER_ERROR_INVALID_PROPERTY, GTK_BUILDER_ERROR_INVALID_SIGNAL, - GTK_BUILDER_ERROR_INVALID_ID + GTK_BUILDER_ERROR_INVALID_ID, + GTK_BUILDER_ERROR_INVALID_FUNCTION } GtkBuilderError; GDK_AVAILABLE_IN_ALL @@ -196,6 +200,19 @@ void gtk_builder_add_callback_symbols (GtkBuilder *builder, GDK_AVAILABLE_IN_ALL GCallback gtk_builder_lookup_callback_symbol (GtkBuilder *builder, const gchar *callback_name); +GDK_AVAILABLE_IN_ALL +GClosure * gtk_builder_create_closure (GtkBuilder *builder, + const char *function_name, + gboolean swapped, + GObject *object, + GError **error); +GDK_AVAILABLE_IN_ALL +GClosure * gtk_builder_create_cclosure (GtkBuilder *builder, + const char *function_name, + gboolean swapped, + GObject *object, + GError **error); + /** -- 2.30.2